a tool for shared writing and social publishing
1import { Metadata } from "next";
2import * as Y from "yjs";
3import * as base64 from "base64-js";
4
5import type { Fact } from "src/replicache";
6import type { Attribute } from "src/replicache/attributes";
7import { YJSFragmentToString } from "components/Blocks/TextBlock/RenderYJSFragment";
8import { Leaflet } from "./Leaflet";
9import { scanIndexLocal } from "src/replicache/utils";
10import { getRSVPData } from "actions/getRSVPData";
11import { PageSWRDataProvider } from "components/PageSWRDataProvider";
12import { getPollData } from "actions/pollActions";
13import { supabaseServerClient } from "supabase/serverClient";
14import { get_leaflet_data } from "app/api/rpc/[command]/get_leaflet_data";
15import { NotFoundLayout } from "components/PageLayouts/NotFoundLayout";
16
17export const preferredRegion = ["sfo1"];
18export const dynamic = "force-dynamic";
19export const fetchCache = "force-no-store";
20
21type Props = {
22 // this is now a token id not leaflet! Should probs rename
23 params: Promise<{ leaflet_id: string }>;
24};
25export default async function LeafletPage(props: Props) {
26 let { result: res } = await get_leaflet_data.handler(
27 { token_id: (await props.params).leaflet_id },
28 { supabase: supabaseServerClient },
29 );
30 let rootEntity = res.data?.root_entity;
31 if (!rootEntity || !res.data || res.data.blocked_by_admin)
32 return (
33 <NotFoundLayout>
34 <p className="font-bold">Sorry, we can't find this leaflet!</p>
35 <p>
36 This may be a glitch on our end. If the issue persists please{" "}
37 <a href="mailto:contact@leaflet.pub">send us a note</a>.
38 </p>
39 </NotFoundLayout>
40 );
41
42 let [{ data }, rsvp_data, poll_data] = await Promise.all([
43 supabaseServerClient.rpc("get_facts", {
44 root: rootEntity,
45 }),
46 getRSVPData(res.data.permission_token_rights.map((ptr) => ptr.entity_set)),
47 getPollData(res.data.permission_token_rights.map((ptr) => ptr.entity_set)),
48 ]);
49 let initialFacts = (data as unknown as Fact<Attribute>[]) || [];
50 return (
51 <PageSWRDataProvider
52 rsvp_data={rsvp_data}
53 poll_data={poll_data}
54 leaflet_id={res.data.id}
55 leaflet_data={res}
56 >
57 <Leaflet
58 initialFacts={initialFacts}
59 leaflet_id={rootEntity}
60 token={res.data}
61 />
62 </PageSWRDataProvider>
63 );
64}
65
66export async function generateMetadata(props: Props): Promise<Metadata> {
67 let { result: res } = await get_leaflet_data.handler(
68 { token_id: (await props.params).leaflet_id },
69 { supabase: supabaseServerClient },
70 );
71 let rootEntity = res.data?.root_entity;
72 if (!rootEntity || !res.data) return { title: "Leaflet not found" };
73 let publication_data =
74 res.data?.leaflets_in_publications?.[0] ||
75 res.data?.permission_token_rights[0].entity_sets?.permission_tokens?.find(
76 (p) => p.leaflets_in_publications.length,
77 )?.leaflets_in_publications?.[0];
78 if (publication_data) {
79 return {
80 title: publication_data.title || "Untitled",
81 description: publication_data.description,
82 };
83 }
84 let { data } = await supabaseServerClient.rpc("get_facts", {
85 root: rootEntity,
86 });
87 let initialFacts = (data as unknown as Fact<Attribute>[]) || [];
88 let scan = scanIndexLocal(initialFacts);
89 let firstPage =
90 scan.eav(rootEntity, "root/page")[0]?.data.value || rootEntity;
91 let pageType = scan.eav(firstPage, "page/type")[0]?.data.value || "doc";
92 let firstBlock, secondBlock;
93 if (pageType === "canvas") {
94 [firstBlock, secondBlock] = scan
95 .eav(firstPage, "canvas/block")
96 .map((b) => {
97 let type = scan.eav(b.data.value, "block/type");
98 if (!type[0]) return null;
99 return {
100 ...b.data,
101 type: type[0].data.value,
102 };
103 })
104 .filter((b) => b !== null)
105 .filter((b) => b.type === "text" || b.type === "heading")
106 .sort((a, b) => {
107 if (a.position.y === b.position.y) {
108 return a.position.x - b.position.x;
109 }
110 return a.position.y - b.position.y;
111 });
112 } else {
113 [firstBlock, secondBlock] = scan
114 .eav(firstPage, "card/block")
115 .map((b) => {
116 let type = scan.eav(b.data.value, "block/type");
117 return {
118 ...b.data,
119 type: type[0]?.data.value,
120 };
121 })
122
123 .filter((b) => b.type === "text" || b.type === "heading")
124 .sort((a, b) => (a.position > b.position ? 1 : -1));
125 }
126 let metadata: Metadata = { title: "Untitled Leaflet", description: " " };
127
128 let titleFact = initialFacts.find(
129 (f) => f.entity === firstBlock?.value && f.attribute === "block/text",
130 ) as Fact<"block/text"> | undefined;
131 if (titleFact) {
132 let doc = new Y.Doc();
133 const update = base64.toByteArray(titleFact.data.value);
134 Y.applyUpdate(doc, update);
135 let nodes = doc.getXmlElement("prosemirror").toArray();
136 metadata.title = YJSFragmentToString(nodes[0]);
137 }
138
139 let descriptionFact = initialFacts.find(
140 (f) => f.entity === secondBlock?.value && f.attribute === "block/text",
141 ) as Fact<"block/text"> | undefined;
142 if (descriptionFact) {
143 let doc = new Y.Doc();
144 const update = base64.toByteArray(descriptionFact.data.value);
145 Y.applyUpdate(doc, update);
146 let nodes = doc.getXmlElement("prosemirror").toArray();
147 metadata.description = YJSFragmentToString(nodes[0]);
148 }
149
150 return metadata;
151}